"""
MRP 计算工具模块

该模块实现了物料需求计划 (MRP) 的核心计算逻辑。
主要功能包括：
1. 根据输入参数计算物料需求计划
2. 生成订单与到货信息表
3. 生成每日库存计划
"""

from datetime import datetime, timedelta
from typing import List, Dict, Tuple
from langchain_core.tools import tool


@tool
def calculate_mrp_tool(
    total_demand: float,
    daily_demand: float,
    order_qty: float,
    reorder_point: float,
    lead_time_days: int,
    start_date: str,
    initial_stock: float,
    max_display_days: int = 30
) -> Tuple[List[Dict], List[Dict]]:
    """
    计算黍米酿造的 MRP 计划（物料需求计划）

    Args:
        total_demand (float): 总需求量（吨）
        daily_demand (float): 日需求量（吨）
        order_qty (float): 订货批量（吨）
        reorder_point (float): 订货点（吨）
        lead_time_days (int): 订货提前期（天）
        start_date (str): 生产开始日期，格式 "YYYY-MM-DD"
        initial_stock (float): 初始库存（吨）
        max_display_days (int): 最多显示多少天的库存计划（默认30）

    Returns:
        tuple: (orders_info, daily_stock_plan)
            - orders_info: List[Dict]，每项含 'order_date', 'arrival_date', 'stock_after_arrival'
            - daily_stock_plan: List[Dict]，每项含 'date', 'start_stock', 'events'
            
    计算逻辑说明：
        1. 根据总需求量和日需求量计算总天数
        2. 模拟每日库存变化过程
        3. 当库存降至订货点以下且没有未完成订单时触发订货
        4. 订货提前期后货物到达，增加库存
        5. 每日消耗固定数量的物料
    """
    # 验证输入参数
    if daily_demand <= 0:
        raise ValueError("日需求量必须大于0")
        
    # 计算总天数（向上取整）
    total_days = int(-(-total_demand // daily_demand))

    # 解析开始日期
    start_dt = datetime.strptime(start_date, "%Y-%m-%d").date()

    # === 第一阶段：模拟运行，确定订单时间 ===
    current_stock = initial_stock
    current_date = start_dt
    orders = []                      # 记录订单 (order_date, arrival_date)
    arrivals = {}                    # date -> total_qty arriving that day
    has_pending_order = False

    # 遍历每一天，模拟库存变化和订货过程
    for day in range(total_days):
        date_key = current_date

        # 1. 早晨到货处理
        if date_key in arrivals:
            current_stock += arrivals[date_key]
            has_pending_order = False

        # 2. 检查是否需要下单（库存低于订货点且没有未完成订单）
        if current_stock <= reorder_point and not has_pending_order:
            order_date = current_date
            arrival_date = current_date + timedelta(days=lead_time_days)
            orders.append((order_date, arrival_date))

            # 注册到货信息
            arrivals[arrival_date] = arrivals.get(arrival_date, 0) + order_qty
            has_pending_order = True

        # 3. 日终消耗处理
        current_stock -= daily_demand
        if current_stock < 0:
            current_stock = 0

        current_date += timedelta(days=1)

    # === 第二阶段：重新模拟，计算每次到货后的库存（用于订单表）===
    arrival_stock_map = {}
    sim_stock = initial_stock
    sim_date = start_dt
    # 聚合同一天的到货量
    arrival_aggregated = {}
    for _, arr_date in orders:
        arrival_aggregated[arr_date] = arrival_aggregated.get(arr_date, 0) + order_qty

    # 模拟计算每次到货后的库存量
    for day in range(total_days):
        d = sim_date
        if d in arrival_aggregated:
            sim_stock += arrival_aggregated[d]
            arrival_stock_map[d] = sim_stock  # 到货后、消耗前的库存
        sim_stock -= daily_demand
        if sim_stock < 0:
            sim_stock = 0
        sim_date += timedelta(days=1)

    # 构建订单信息列表
    orders_info = []
    for i, (ord_date, arr_date) in enumerate(orders, 1):
        stock_after = arrival_stock_map.get(arr_date, order_qty)  # fallback
        orders_info.append({
            "序号": i,
            "order_date": ord_date.strftime("%Y.%m.%d"),
            "arrival_date": arr_date.strftime("%Y.%m.%d"),
            "stock_after_arrival": round(stock_after, 2)
        })

    # === 第三阶段：生成每日初始库存计划（最多 max_display_days 天）===
    display_days = min(total_days, max_display_days)
    daily_stock_plan = []

    display_stock = initial_stock
    display_date = start_dt

    # 预处理事件映射，提高查找效率
    arrival_days = set(arrival_aggregated.keys())
    order_days = {ord_date for ord_date, _ in orders}

    # 逐日生成库存计划
    for i in range(display_days):
        events = []

        # 早晨到货处理（计入当日初始库存）
        if display_date in arrival_days:
            qty = arrival_aggregated[display_date]
            display_stock += qty
            events.append("【到货】")

        start_stock = display_stock  # 记录当日初始库存

        # 检查是否为订货日
        if display_date in order_days:
            events.append("【订货】")

        # 日终消耗处理（影响下一天）
        display_stock -= daily_demand
        if display_stock < 0:
            display_stock = 0

        # 添加到计划列表
        daily_stock_plan.append({
            "date": display_date.strftime("%-m.%-d"),  # 去掉前导零，如 9.1
            "start_stock": round(start_stock, 2),
            "events": "".join(events)
        })

        display_date += timedelta(days=1)

    return orders_info, daily_stock_plan